package simulator;

import java.io.FileNotFoundException;
import java.util.HashMap;
import java.util.Map.Entry;

import jsonreader.*;

/**
 * This class interprets the data parsed from a JSON file and stores it to be accessible throughout the program
 * The use of static here is less than perfect, and should really be replaced by some thorough dependency-injection
 * However, the data stored here is required in GameState, and we can't store a reference to the object in every GameState
 *
 */
public class GameDataStore {
	static private HashMap<GameAsset, GameObject> gameObjects = new HashMap<GameAsset, GameObject>();
	
	/**
	 * This function gets the GameObject for the specified GameAsset value
	 * @param asset - the GameObject for this GameAsset is returned
	 * @return returns the specified asset from the array. Or null if it's not implemented in the file or the file has not been read yet
	 */
	static public GameObject getGameObjectFromGameAsset(GameAsset asset) {
		return gameObjects.get(asset);
	}
	
	/**
	 * Loads the unit / building data from the JSON file and stores it in the GameObject format
	 * @param fileName
	 * @throws FileNotFoundException
	 * @throws JSONParseException
	 * @throws StarcraftDataParseException
	 */
	static public void loadDataFromJSON(String fileName) throws FileNotFoundException, JSONParseException, StarcraftDataParseException {
		
		JSONValue jsonFile = JSONParser.parseFile(fileName);
		HashMap<String, JSONValue> units = jsonFile.getObject();
		
		if(units == null) {
			throw new StarcraftDataParseException("JSON file " + fileName + " not in expected format, refer to examples");
		}
		
		for(Entry<String, JSONValue> entry : units.entrySet()) {
			if(entry.getValue().getType() == JSONType.OBJECT) {
				
				GameAsset assetValue = null;
				
				try {
					assetValue = GameAsset.valueOf(entry.getKey());
				}
				catch(IllegalArgumentException e) {
					throw new StarcraftDataParseException("Found object with key " + entry.getKey() + " which is not a valid GameAsset name");
				}
				
				HashMap<String, JSONValue> objectProperties = entry.getValue().getObject();
				
				if(objectProperties == null) {
					throw new StarcraftDataParseException("Starcraft json object " + entry.getKey() + " not in expected format, refer to examples");
				}
				
				GameObject obj = new GameObject(assetValue);
				
				for(Entry<String, JSONValue> innerEntry : objectProperties.entrySet()) {
					String key = innerEntry.getKey();
					
					if(key.equals("name")) {
						obj.name = innerEntry.getValue().getString();
						if(obj.name == null) {
							throw new StarcraftDataParseException("Starcraft json object " + entry.getKey() + ", property " + key + " not in expected format, refer to examples");
						}
					}
					else if(key.equals("mineral_cost")) {
						
						Double value = innerEntry.getValue().getNumber();
						
						if(value == null) {
							throw new StarcraftDataParseException("Starcraft json object " + entry.getKey() + ", property " + key + " not in expected format, refer to examples");
						}
						
						obj.mineralCost = (int) Math.round(value);
					}
					else if(key.equals("vespene_cost")) {
						
						Double value = innerEntry.getValue().getNumber();
						
						if(value == null) {
							throw new StarcraftDataParseException("Starcraft json object " + entry.getKey() + ", property " + key + " not in expected format, refer to examples");
						}
						
						obj.vespeneCost = (int) Math.round(value);
					}
					else if(key.equals("build_time")) {
						
						Double value = innerEntry.getValue().getNumber();
						
						if(value == null) {
							throw new StarcraftDataParseException("Starcraft json object " + entry.getKey() + ", property " + key + " not in expected format, refer to examples");
						}
						
						obj.buildTime = (int) Math.round(value);
					}
					else if(key.equals("supply")) {
						
						Double value = innerEntry.getValue().getNumber();
						
						if(value == null) {
							throw new StarcraftDataParseException("Starcraft json object " + entry.getKey() + ", property " + key + " not in expected format, refer to examples");
						}
						
						obj.supply = (int) Math.round(value);
					}
					else if(key.equals("build_slots")) {
						
						Double value = innerEntry.getValue().getNumber();
						
						if(value == null) {
							throw new StarcraftDataParseException("Starcraft json object " + entry.getKey() + ", property " + key + " not in expected format, refer to examples");
						}
						obj.buildSlots = (int) Math.round(value);
					}
					else if(key.equals("occupy_workers")) {
						Boolean value = innerEntry.getValue().getBoolean();
						
						if(value == null) {
							throw new StarcraftDataParseException("Starcraft json object " + entry.getKey() + ", property " + key + " not in expected format, refer to examples");
						}
						obj.occupyWorkers = value;
					}
					else if(key.equals("dependencies")) {
						
						HashMap<GameAsset, Integer> dependencyMap = new HashMap<GameAsset, Integer>();
						
						if(innerEntry.getValue().getType() == JSONType.ARRAY) {
							JSONValue[] value = innerEntry.getValue().getArray();
							
							if(value == null) {
								throw new StarcraftDataParseException("Starcraft json object " + entry.getKey() + ", property " + key + " not in expected format, refer to examples");
							}
							
							for(int i=0; i<value.length; i++) {
								if(value[i].getType() == JSONType.STRING) {
									GameAsset dependencyAsset = null;
									try {
										dependencyAsset = GameAsset.valueOf(value[i].getString());
									}
									catch(IllegalArgumentException e) {
										throw new StarcraftDataParseException("Dependency listed which doesn't match a GameAsset: " + value[i].getString());
									}
									dependencyMap.put(dependencyAsset, (dependencyMap.get(dependencyAsset) == null) ? 1: dependencyMap.get(dependencyAsset) + 1);
								}
							}
						}
						obj.dependencies = dependencyMap;
					}
					else if(key.equals("blocking_dependencies")) {
						HashMap<GameAsset, Integer> dependencyMap = new HashMap<GameAsset, Integer>();
						
						if(innerEntry.getValue().getType() == JSONType.ARRAY) {
							JSONValue[] value = innerEntry.getValue().getArray();
							
							if(value == null) {
								throw new StarcraftDataParseException("Starcraft json object " + entry.getKey() + ", property " + key + " not in expected format, refer to examples");
							}
							for(int i=0; i<value.length; i++) {
								
								if(value[i].getType() == JSONType.STRING) {
									
									GameAsset dependencyAsset = null;
									
									try {
										dependencyAsset = GameAsset.valueOf(value[i].getString());
									}
									catch(IllegalArgumentException e) {
										throw new StarcraftDataParseException("Blocking Dependency listed which doesn't match a GameAsset: " + value[i].getString());
									}
									dependencyMap.put(dependencyAsset, (dependencyMap.get(dependencyAsset) == null) ? 1: dependencyMap.get(dependencyAsset) + 1);
								}
							}
							
						}
						obj.blockingDependencies = dependencyMap;
					}
				}
				GameDataStore.gameObjects.put(assetValue, obj);
			}
		}
	}
}
